package com.bac.rentmap.osmimport;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *
 * @author Ivan Holub
 */
public class OsmContentHandler extends DefaultHandler {

    private HashMap<String, OsmNode> nodes = new HashMap<String, OsmNode>();

    private List<OsmBuilding> buildings = new ArrayList<OsmBuilding>();

    private OsmWay currentWay;

    private boolean isInsideWay;

    private Map<String, OsmHighway> highwaysMap = new HashMap<String, OsmHighway>();

    private OsmBbox bbox;

    public OsmContentHandler(OsmBbox bbox) {
        this.bbox = bbox;
    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        if ("node".equals(qName)) {
            String lat = attributes.getValue("lat");
            String lon = attributes.getValue("lon");
            if (bbox.isInside(lat, lon)) {
                String id = attributes.getValue("id");
                OsmNode node = new OsmNode();
                node.setId(id);
                node.setLat(lat);
                node.setLon(lon);
                nodes.put(id, node);
            }
        } else if ("way".equals(qName)) {
            currentWay = new OsmWay();
            isInsideWay = true;
        } else if ("nd".equals(qName)) {
            String nodeId = attributes.getValue("ref");
            currentWay.addNodeRef(nodeId);
        } else if ("tag".equals(qName)) {
            if (isInsideWay) {
                String k = attributes.getValue("k");
                String v = attributes.getValue("v");
                if ("highway".equals(k)) {
                    currentWay.setHighway(true);
                } else if ("name".equals(k)) {
                    currentWay.setName(v);
                } else if ("building".equals(k)) {
                    currentWay.setBuilding(true);    
                } else if ("addr:housenumber".equals(k)) {
                    currentWay.setHousenumber(v);
                } else if ("addr:street".equals(k)) {
                    currentWay.setStreet(v);
                }
            }
        }
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        if ("way".equals(qName)) {
            String name = currentWay.getName();
            if (currentWay.isBuilding() && currentWay.getHousenumber() != null && currentWay.getStreet() != null) {
                OsmBuilding b = new OsmBuilding();
                b.setHousenumber(currentWay.getHousenumber());
                b.setStreet(currentWay.getStreet());
                boolean isUpdated = findBuildingLatLon(currentWay, b);
                if (isUpdated) {
                    buildings.add(b);
                }
            } else if (currentWay.isHighway() && name != null) {
                OsmHighway highway = highwaysMap.get(name);
                boolean isNew = false;
                if (highway == null) {
                    isNew = true;
                    highway = new OsmHighway();
                    highway.setName(name);                    
                }

                HighwayRange highwayRange = highway.getHighwayRange();
                boolean isUpdated = updateHighwayRange(currentWay, highwayRange);
                if (isUpdated) {
                    if (isNew) {
                        highwaysMap.put(name, highway);
                    }
                }
            }
            isInsideWay = false;
            currentWay = null;
        }
    }

    public HashMap<String, OsmNode> getNodes() {
        return nodes;
    }

    private boolean updateHighwayRange(OsmWay way, HighwayRange highwayRange) {
        List<String> nodesRef = way.getNodesRef();
        boolean isUpdated = false;
        for (String nodeId : nodesRef) {
            OsmNode node = nodes.get(nodeId);
            if (node != null) {
                isUpdated = true;
                String lat = node.getLat();
                String lon = node.getLon();
                highwayRange.acceptLat(lat);
                highwayRange.acceptLon(lon);
            }
        }
        return isUpdated;
    }

    public Map<String, OsmHighway> getHighwaysMap() {
        return highwaysMap;
    }

    public List<OsmBuilding> getBuildings() {
        return buildings;
    }

    private boolean findBuildingLatLon(OsmWay way, OsmBuilding building) {
        List<String> nodesRef = way.getNodesRef();
        boolean isUpdated = false;
        for (String nodeId : nodesRef) {
            OsmNode node = nodes.get(nodeId);
            if (node != null) {
                isUpdated = true;
                String lat = node.getLat();
                String lon = node.getLon();
                building.setLat(lat);
                building.setLon(lon);
                break;
            }
        }
        return isUpdated;
    }

}